<!DOCTYPE html>
<html>

<head>
  <title>Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions</title>
  <script id="adobe_dtm" src="https://www.redhat.com/dtm.js" type="text/javascript"></script>
  <script src="/assets/javascript/highlight.pack.js" type="text/javascript"></script>
  <META HTTP-EQUIV='Content-Security-Policy' CONTENT="default-src 'none'; script-src 'self' 'unsafe-eval' 'sha256-ANpuoVzuSex6VhqpYgsG25OHWVA1I+F6aGU04LoI+5s=' 'sha256-ipy9P/3rZZW06mTLAR0EnXvxSNcnfSDPLDuh3kzbB1w=' js.bizographics.com https://www.redhat.com assets.adobedtm.com jsonip.com https://ajax.googleapis.com https://www.googletagmanager.com https://www.google-analytics.com https://use.fontawesome.com; style-src 'self' https://fonts.googleapis.com https://use.fontawesome.com; img-src 'self' *; media-src 'self' ; frame-src https://www.googletagmanager.com https://www.youtube.com; frame-ancestors 'none'; base-uri 'none'; object-src 'none'; form-action 'none'; font-src 'self' https://use.fontawesome.com https://fonts.gstatic.com;">
  <META HTTP-EQUIV='X-Frame-Options' CONTENT="DENY">
  <META HTTP-EQUIV='X-XSS-Protection' CONTENT="1; mode=block">
  <META HTTP-EQUIV='X-Content-Type-Options' CONTENT="nosniff">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Quarkus: Supersonic Subatomic Java">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@QuarkusIO"> 
  <meta name="twitter:creator" content="@QuarkusIO">
  <meta property="og:url" content="https://quarkus.io/blog/native-command-mode-tools/" />
  <meta property="og:title" content="Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions" />
  <meta property="og:description" content="Quarkus: Supersonic Subatomic Java" />
  <meta property="og:image" content="/assets/images/quarkus_card.png" />
  <link rel="canonical" href="https://quarkus.io/blog/native-command-mode-tools/">
  <link rel="shortcut icon" type="image/png" href="/favicon.ico" >
  <link rel="stylesheet" href="https://quarkus.io/guides/stylesheet/config.css" />
  <link rel="stylesheet" href="/assets/css/main.css" />
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
  <link rel="alternate" type="application/rss+xml"  href="https://quarkus.io/feed.xml" title="Quarkus">
  <script src="https://quarkus.io/assets/javascript/goan.js" type="text/javascript"></script>
  <script src="https://quarkus.io/assets/javascript/hl.js" type="text/javascript"></script>
</head>

<body class="post">
  <!-- Google Tag Manager (noscript) -->
  <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NJWS5L"
  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
  <!-- End Google Tag Manager (noscript) -->

  <div class="nav-wrapper">
  <div class="grid-wrapper">
    <div class="width-12-12">
      <input type="checkbox" id="checkbox" />
      <nav id="main-nav" class="main-nav">
  <div class="container">
    <div class="logo-wrapper">
      
        <a href="/"><img src="/assets/images/quarkus_logo_horizontal_rgb_600px_reverse.png" class="project-logo" title="Quarkus"></a>
      
    </div>
    <label class="nav-toggle" for="checkbox">
      <i class="fa fa-bars"></i>
    </label>
    <div id="menu" class="menu">
      <span>
        <a href="/get-started/" class="">Get Started</a>
      </span>
      <span>
        <a href="/guides/" class="">Guides</a>
      </span>
      <span>
        <a href="/community/" class="">Community</a>
      </span>
      <span>
        <a href="/support/" class="">Support</a>
      </span>
      <span>
        <a href="/blog/" class="active">Blog</a>
      </span>
      <span>
        <a href="https://code.quarkus.io" class="button-cta secondary white">Start Coding</a>
      </span>
    </div>
  </div>
      </nav>
    </div>
  </div>
</div>

  <div class="content">
    <div class="post-page grid-wrapper">
  <div class="width-8-12 width-12-12-m doc-content">
    <div class="grid-wrapper">
      <div class="width-12-12">
        <p>
          <a href="/blog"><i class="fas fa-angle-left"></i> Back to all posts</a>
        </p>
      </div>
      <div class="width-12-12">
        <div class="post-date">
          May 12, 2020 
          
            <span class="tags"><a href="/blog/tag/development-tips">#development-tips</a></span>
          
        </div>
        <h1 class="post-title">Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions</h1>
        <div class="grid-wrapper">
          <div class="width-8-12 width-12-12-m byline-wrapper">
            
            
              <img class="headshot" src="https://www.gravatar.com/avatar/0ae56c62d1b2142ba2868725b956b7ab">
            
            <p class="byline">By Daniel Platz</p>
          </div>
          <div class="width-12-12">
              <div class="paragraph">
<p>Quarkus is advertised as a cloud-native runtime for Microservices but the <a href="https://quarkus.io/blog/quarkus-1-4-final-released/">introduction of command-mode</a> might have opened up new and interesting spaces where it could find adoption.</p>
</div>
<div class="paragraph">
<p>In this blog post I will present a few ideas about the possible usage of command-mode in Quarkus that you might find useful to consider when developing your next tool.
If you have not heard about command-mode, I suggest to start reading <a href="https://quarkus.io/blog/introducing-command-mode/">this introduction</a> first and then come back.</p>
</div>
<div class="paragraph">
<p>Ready? Ok, let&#8217;s continue.</p>
</div>
<div class="sect1">
<h2 id="cross-platform-builds-via-github-actions"><a class="anchor" href="#cross-platform-builds-via-github-actions"></a>Cross-platform builds via Github Actions</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Command-line tools like <code>kubectl</code> are often written in languages like C, Go or Rust and compiled to native executables for various target platforms.
This is great because you don&#8217;t need to rely on Java installed; or being installed in a wrong version.</p>
</div>
<div class="paragraph">
<p>With GraalVM native-images, this argument against Java does not hold anymore. Quarkus supports the build of native executables via GraalVM; mainly for building natively for Linux containers but there is experimental support for OS X and Windows as well.
Using the native build in combination with command-mode sounds like a very interesting and productive choice to write command-line tools.</p>
</div>
<div class="paragraph">
<p>But how do you build your native executables for Linux, Mac and Windows?
If you are hosting your sources as a Github project, I can tell you that it is super-simple to get started. Github offers <a href="https://help.github.com/en/actions/building-and-testing-code-with-continuous-integration/setting-up-continuous-integration-using-github-actions"> Actions</a> which is a free service like <a href="https://travis-ci.org/">Travis CI</a> but fully integrated into the Github experience. Also, it offers runners (i.e. the build agents) for Linux, Mac and Windows.</p>
</div>
<div class="paragraph">
<p>To provide a native release-build for linux, place the following file under <code>.github/workflows</code> in your repo:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="yaml" class="language-yaml hljs">name: release-build

on:
  release:
    types: [created] <i class="conum" data-value="1"></i><b>(1)</b>

jobs:
  build:

    runs-on: ubuntu-latest <i class="conum" data-value="2"></i><b>(2)</b>

    steps:
    - uses: actions/checkout@v2
    - name: Install graalvm
      uses: DeLaGuardo/setup-graalvm@3
      with:
        graalvm-version: '20.0.0.java11'
    - name: Install native-image
      run: gu install native-image
    - name: Set version
      run: ./mvnw versions:set -DnewVersion="${{ github.event.release.tag_name }}" <i class="conum" data-value="3"></i><b>(3)</b>
    - name: Build native executable
      run: ./mvnw package -Dnative <i class="conum" data-value="4"></i><b>(4)</b>
    - name: Upload native executable
      id: upload-native-executable
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ github.event.release.upload_url }}
        asset_path: ./target/quarkus-ls-${{ github.event.release.tag_name }}-runner <i class="conum" data-value="5"></i><b>(5)</b>
        asset_name: quarkus-ls-${{ github.event.release.tag_name }}-linux
        asset_content_type: application/octet-stream</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This build-job is triggered when a new release is triggered. If your are the owner, you will have the button "Draft a new release" under <a href="https://github.com/38leinaD/quarkus-native-app-starter/releases">releases</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Uses a ubuntu runner. See <a href="https://github.com/actions/virtual-environments">GitHub Actions Virtual Environments</a> for a list of all supported environments/runners.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Set version in <code>pom.xml</code> based on specified release version</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Build the native executable</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Upload/attach the artifact to the release</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Building for Mac just means to provide another job with <code>runs-on: macos-latest</code>.</p>
</div>
<div class="paragraph">
<p>Windows is essentially the same but requires a little more attention and knowledge about the tooling. Getting it working required some investigation on how the native build depends on Visual Studio. Also,the Windows runners on Github Actions have a too small page-file by default. That needs increasing as the memory consumption is rather high when running the GraalVMs <code>native-image</code> command.
An full example of a working native-image build under windows can be found <a href="https://github.com/38leinaD/quarkus-native-app-starter/blob/master/.github/workflows/release-build-native-win64.yml">here</a>.</p>
</div>
<div class="paragraph">
<p>After triggering a release from the Github UI, the jobs for all target environments are triggered. The artifacts for all platforms are will be available within minutes and can be consumed by your users. Building, hosting: 100% free and no external service or registration needed.</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="/assets/images/posts/quarkus-native-app/github-actions.jpg" alt="github actions"></span></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="command-mode-for-ui-based-applications"><a class="anchor" href="#command-mode-for-ui-based-applications"></a>Command-mode for UI-based applications</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Another exciting thing about command-mode is that it not only allows to write nice command-line tools (using e.g. <a href="https://picocli.info/">Picocli</a>, <a href="http://aeshell.github.io/">Aesh</a> or <a href="https://commons.apache.org/proper/commons-cli/">Apache Commons CLI</a>) but in general allows to have more control over the lifecycle of an application.
With the possibility to embed your HTML/Javascript frontend, we can even build <a href="https://www.electronjs.org/">Electron-style</a> desktop application with rich user interfaces for our tools. As even JSF most likely will be <a href="https://github.com/adminfaces/quarkus-admin-starter">coming to Quarkus</a>, there is a great variety of choices on how to build a frontend.</p>
</div>
<div class="paragraph">
<p>A very simple scaffold does not require much. If you have <code>quarkus-resteasy</code> as a dependency, the embedded server will be started on port <code>8080</code> just like in any regular Quarkus backend application.
As your tools might be running on a developer system alongside other app-servers or Quarkus instances, it is a good idea to choose a different port to not run into conflicts.</p>
</div>
<div class="paragraph">
<p>Configuring <code>quarkus.http.port=0</code> in your <code>application.properties</code> will make Quarkus choose a random but free port.
How to know from within your application which port it is? When injecting the config-property via <code>@ConfigProperty(name = "quarkus.http.port")</code>, you will get the assigned port.</p>
</div>
<div class="paragraph">
<p>Assuming you have your front-end under <code>src/main/resources/META-INF/resources</code> (in my example it is plain HTML with Javascript) and using a very old Java API (<code>Desktop.getDesktop().browse(&#8230;&#8203;)</code>), you can spin up the default browser to your app like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">import java.awt.Desktop;
import java.net.URI;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class HelloMain implements QuarkusApplication {

    @ConfigProperty(name = "quarkus.http.port")
    Integer assignedPort; <i class="conum" data-value="1"></i><b>(1)</b>

    public static void main(String[] args) {
        Quarkus.run(HelloMain.class, args);
    }

    @Override
    public int run(String... args) throws Exception {
        URI webappUri = new URI("http://localhost:" + assignedPort + "/index.html"); <i class="conum" data-value="2"></i><b>(2)</b>

        Desktop.getDesktop().browse(webappUri);
        Quarkus.waitForExit(); <i class="conum" data-value="3"></i><b>(3)</b>

        return 0;
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Get the assigned port</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Open the <code>index.html</code> of your webapp with the default browser.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Do not exit immediately but wait until the browser/tab is closed. See below for options how to do this.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>There are some more considerations here to make it work reliably.</p>
</div>
<div class="paragraph">
<p>Ending the application when the browser window closes requires some feedback. There are simple ways to achieve this in a semi-reliabe way (see the usage of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event">unload</a> in <a href="https://github.com/38leinaD/quarkus-native-app-starter/blob/master/src/main/resources/META-INF/resources/index.html#L89">index.html</a> by calling <a href="https://github.com/38leinaD/quarkus-native-app-starter/blob/master/src/main/java/org/acme/LsResource.java#L38">a rest endpoint</a>).
For Desktop-class applications, one might have to control the browser process more closely; or, like Electron, even package an own version of Chrome.</p>
</div>
<div class="paragraph">
<p>A problem I was facing is that <code>Desktop.getDesktop().browse(&#8230;&#8203;)</code> does not seem to work reliably when run in native mode with the GraalVM version I was using (<a href="https://github.com/oracle/graal/issues/2430">issue</a>). The alternative, like already stated before, is to control the browser more directly and for example open a Chrome window via <a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exec(java.lang.String)">Runtime.exec(&#8230;&#8203;)</a>. See the <a href="https://github.com/38leinaD/quarkus-native-app-starter/blob/master/src/main/java/org/acme/LsMain.java">main method</a> of the starter project for some possibilities to experiment with.</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="/assets/images/posts/quarkus-native-app/quarkus-native-webui.gif" alt="quarkus native webui"></span></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="summary"><a class="anchor" href="#summary"></a>Summary</h2>
<div class="sectionbody">
<div class="paragraph">
<p>It will be interesting to see where people are taking command-mode. Maybe we will be seeing Electron-style Desktop applications being developed based on Quarkus?
At least to me, this sounds promising and productive. No wasting of time to learn a completely different stack just to write a small tool. Instead, use the same stack and API I am familar with from backend development anyway.</p>
</div>
<div class="paragraph">
<p>We have seen that making a tool available in a way that is easy to consume by people can be achived with Github and Github Actions alone.</p>
</div>
<div class="paragraph">
<p>In case you are interested in a more complete tool where I have used the ideas presented here, check out my <a href="https://github.com/38leinaD/bpmn-diff">bpmn-diff</a> project which is a Git difftool for BPMN files. As an added benefit, it uses Gradle as opposed to Maven and thus provides additional examples on the use of Github Actions.</p>
</div>
</div>
</div>
              
          </div>
          <div class="width-12-12"><div class="share-page">
  <a class="share-linkedin" href="https://www.linkedin.com/shareArticle?mini=true&url=https://quarkus.io/blog/native-command-mode-tools/&title=Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions" rel="nofollow" target="_blank" title="Share on LinkedIn">
    <img src="/assets/images/share-page/icons_social-linkedin.png"/>
  </a>
  <a class="share-twitter" href="https://twitter.com/intent/tweet?text=Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions&url=https://quarkus.io/blog/native-command-mode-tools/&via=quarkusio&related=quarkusio" rel="nofollow" target="_blank" title="Share on Twitter">
    <img src="/assets/images/share-page/icons_social-twitter.png"/>
  </a>
  <a class="share-facebook" href="https://facebook.com/sharer.php?u=https://quarkus.io/blog/native-command-mode-tools/" rel="nofollow" target="_blank" title="Share on Facebook">
    <img src="/assets/images/share-page/icons_social-facebook.png"/>
  </a>
  <a class="share-reddit" href="http://www.reddit.com/submit?url=https://quarkus.io/blog/native-command-mode-tools/" onclick="window.open(this.href, 'pop-up', 'left=20,top=20,width=900,height=500,toolbar=1,resizable=0'); return false;" title="Share on Reddit" >
    <img src="/assets/images/share-page/icons_social-reddit.png"/>
  </a>
  <a class="share-email" href="mailto:?subject=Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions&amp;body=Ideas on Quarkus command-mode, UI-based apps and releasing via Github Actions https://quarkus.io/blog/native-command-mode-tools/" title="Share via Email" >
    <img src="/assets/images/share-page/icons_social-email.png"/>
  </a>
</div>
</div>
        </div>
      </div>
    </div>
  </div>
</div>

  </div>

  <div class="content project-footer">
  <div class="footer-section">
    <div class="logo-wrapper">
      <a href="/"><img src="/assets/images/quarkus_logo_horizontal_rgb_reverse.svg" class="project-logo" title="Quarkus"></a>
    </div>
  </div>
  <div class="grid-wrapper">
    <p class="grid__item width-3-12">Quarkus is open. All dependencies of this project are available under the <a href='https://www.apache.org/licenses/LICENSE-2.0' target='_blank'>Apache Software License 2.0</a> or compatible license.<br /><br />This website was built with <a href='https://jekyllrb.com/' target='_blank'>Jekyll</a>, is hosted on <a href='https://pages.github.com/' target='_blank'>Github Pages</a> and is completely open source. If you want to make it better, <a href='https://github.com/quarkusio/quarkusio.github.io' target='_blank'>fork the website</a> and show us what you’ve got.</p>

    
      <div class="width-1-12 project-links">
        <span>Navigation</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="/">Home</a></li>
          
            <li><a href="/guides">Guides</a></li>
          
            <li><a href="/community/#contributing">Contribute</a></li>
          
            <li><a href="/faq">FAQ</a></li>
          
            <li><a href="/get-started">Get Started</a></li>
          
        </ul>
      </div>
    
      <div class="width-1-12 project-links">
        <span>Contribute</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="https://twitter.com/quarkusio">Follow us</a></li>
          
            <li><a href="https://github.com/quarkusio">GitHub</a></li>
          
            <li><a href="/security">Security&nbsp;policy</a></li>
          
        </ul>
      </div>
    
      <div class="width-1-12 project-links">
        <span>Get Help</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="https://groups.google.com/forum/#!forum/quarkus-dev">Forums</a></li>
          
            <li><a href="https://quarkusio.zulipchat.com">Chatroom</a></li>
          
        </ul>
      </div>
    

    
      <div class="width-3-12 more-links">
        <span>Quarkus is made of community projects</span>
        <ul class="footer-links">
          
            <li><a href="https://vertx.io/" target="_blank">Eclipse Vert.x</a></li>
          
            <li><a href="https://microprofile.io" target="_blank">Eclipse MicroProfile</a></li>
          
            <li><a href="https://hibernate.org" target="_blank">Hibernate</a></li>
          
            <li><a href="https://netty.io" target="_blank">Netty</a></li>
          
            <li><a href="https://resteasy.github.io" target="_blank">RESTEasy</a></li>
          
            <li><a href="https://camel.apache.org" target="_blank">Apache Camel</a></li>
          
            <li><a href="https://code.quarkus.io/" target="_blank">And many more...</a></li>
          
        </ul>
      </div>
    
  </div>
</div>
  <div class="content redhat-footer">
  <div class="grid-wrapper">
    <span class="licence">
      <i class="fab fa-creative-commons"></i><i class="fab fa-creative-commons-by"></i> <a href="https://creativecommons.org/licenses/by/3.0/" target="_blank">CC by 3.0</a> | <a href="https://www.redhat.com/en/about/privacy-policy">Privacy Policy</a>
    </span>
    <span class="redhat">
      Sponsored by
    </span>
    <span class="redhat-logo">
      <a href="https://www.redhat.com/" target="_blank"><img src="/assets/images/redhat_reversed.svg"></a>
    </span>
  </div>
</div>


  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" integrity="sha384-8gBf6Y4YYq7Jx97PIqmTwLPin4hxIzQw5aDmUg/DDhul9fFpbbLcLh3nTIIDJKhx" crossorigin="anonymous"></script>
  <script type="text/javascript" src="/assets/javascript/mobile-nav.js"></script>
  <script type="text/javascript" src="/assets/javascript/scroll-down.js"></script>
  <script src="/assets/javascript/satellite.js" type="text/javascript"></script>
  <script src="https://quarkus.io/guides/javascript/config.js" type="text/javascript"></script>
  <script src="/assets/javascript/search-filter.js" type="text/javascript"></script>
  <script src="/assets/javascript/back-to-top.js" type="text/javascript"></script>
</body>

</html>
